﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;


namespace Infrastructure
{
    /// <summary>
    /// 用于生成动态匿名类型
    /// <remarks></remarks>
    /// </summary>
    public static class DynamicType
    {
        /// <summary>
        /// 属性信息
        /// </summary>
        public class Property
        {
            /// <summary>
            /// 属性名
            /// </summary>
            public string Name { get; set; }
            /// <summary>
            /// 属性类型
            /// </summary>
            public Type PropertyType { get; set; }

            /// <summary>
            /// 属性信息
            /// </summary>
            /// <param name="name">属性名</param>
            /// <param name="propertyType">属性类型</param>
            public Property(string name, Type propertyType)
            {
                this.Name = name;
                this.PropertyType = propertyType;
            }

            /// <summary>
            /// 是否和目标属性相等
            /// </summary>
            /// <param name="obj">目标属性</param>
            /// <returns></returns>
            public bool Equals(Property obj)
            {
                if (this.Name != null && this.PropertyType != null)
                {
                    return this.Name.Equals(obj.Name) && this.PropertyType.Equals(obj.PropertyType);
                }
                return false;
            }

            /// <summary>
            /// 从数据表的列信息获得
            /// </summary>
            /// <param name="columns">列信息</param>
            /// <exception cref="ArgumentException"></exception>
            /// <returns></returns>
            public static Property[] FromColumns(DataColumnCollection columns)
            {
                if (columns == null)
                {
                    throw new ArgumentException();
                }

                int i = 0;
                Property[] propertyies = new Property[columns.Count];
                foreach (DataColumn c in columns)
                {
                    propertyies[i] = new Property(c.ColumnName, c.DataType);
                    i++;
                }

                return propertyies;
            }
        }

        /// <summary>
        /// 动态类型信息
        /// </summary>
        private class DynamicTypeInfo
        {
            /// <summary>
            /// 生成后的类型
            /// </summary>
            public Type BuildType { get; set; }
            /// <summary>
            /// 属性信息
            /// </summary>
            public Property[] Properties { get; set; }

            /// <summary>
            /// 属性是还和目标相等
            /// </summary>
            /// <param name="properties">目标属性</param>
            /// <returns></returns>
            public bool PropertyEquals(Property[] properties)
            {
                if (this.Properties != null && properties != null)
                {
                    if (this.Properties.Length == properties.Length)
                    {
                        for (int i = 0; i < this.Properties.Length; i++)
                        {
                            if (this.Properties[i].Equals(properties[i]) == false)
                            {
                                return false;
                            }
                        }
                        return true;
                    }
                    return false;
                }

                return this.Properties == properties;
            }
        }

        /// <summary>
        /// 线程同步锁
        /// </summary>
        private static object SyncRoot = new object();

        /// <summary>
        /// 动态类型信息列表
        /// </summary>
        private static List<DynamicTypeInfo> DynamicTypeInfoList = new List<DynamicTypeInfo>();

        /// <summary>
        /// 动态创建类型
        /// </summary>
        /// <param name="assemblyName">所在程序集名</param>
        /// <param name="moduleName">模块名</param>
        /// <param name="typeName">类型名</param>
        /// <returns></returns>
        private static TypeBuilder NewTypeBuilder(string assemblyName, string moduleName, string typeName)
        {
            TypeBuilder typeBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(assemblyName), AssemblyBuilderAccess.Run).DefineDynamicModule(moduleName).DefineType(typeName, TypeAttributes.Public);
            typeBuilder.DefineDefaultConstructor(MethodAttributes.Public);
            return typeBuilder;
        }

        /// <summary>
        /// 为类型添加属性
        /// </summary>
        /// <param name="builder">类型创建器</param>
        /// <param name="propertyName">属性名</param>
        /// <param name="propertyType">属性类型</param>
        /// <returns></returns>
        private static void AddProperty(TypeBuilder builder, string propertyName, Type propertyType)
        {
            const string PrivateFieldPrefix = "m_";
            const string GetterPrefix = "get_";
            const string SetterPrefix = "set_";

            FieldBuilder fieldBuilder = builder.DefineField(string.Concat(PrivateFieldPrefix, propertyName), propertyType, FieldAttributes.Private);
            PropertyBuilder propertyBuilder = builder.DefineProperty(propertyName, System.Reflection.PropertyAttributes.HasDefault, propertyType, null);

            MethodAttributes propertyMethodAttributes = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
            MethodBuilder getterMethod = builder.DefineMethod(string.Concat(GetterPrefix, propertyName), propertyMethodAttributes, propertyType, Type.EmptyTypes);

            ILGenerator getterILCode = getterMethod.GetILGenerator();
            getterILCode.Emit(OpCodes.Ldarg_0);
            getterILCode.Emit(OpCodes.Ldfld, fieldBuilder);
            getterILCode.Emit(OpCodes.Ret);

            MethodBuilder setterMethod = builder.DefineMethod(string.Concat(SetterPrefix, propertyName), propertyMethodAttributes, null, new Type[] { propertyType });
            ILGenerator setterILCode = setterMethod.GetILGenerator();
            setterILCode.Emit(OpCodes.Ldarg_0);
            setterILCode.Emit(OpCodes.Ldarg_1);
            setterILCode.Emit(OpCodes.Stfld, fieldBuilder);
            setterILCode.Emit(OpCodes.Ret);
            propertyBuilder.SetGetMethod(getterMethod);
            propertyBuilder.SetSetMethod(setterMethod);
        }

        /// <summary>
        /// 创建一个新的动态类型
        /// </summary>
        /// <param name="properties">包含的属性信息</param>
        /// <returns></returns>
        private static Type NewType(params Property[] properties)
        {
            var typeBuilder = DynamicType.NewTypeBuilder("EmitAssembly", "EmitMoudle", Guid.NewGuid().ToString());

            if (properties != null)
            {
                foreach (var p in properties)
                {
                    DynamicType.AddProperty(typeBuilder, p.Name, p.PropertyType);
                }
            }

            var type = typeBuilder.CreateType();
            return type;
        }

        /// <summary>
        /// 创建动态类型
        /// </summary>
        /// <param name="properties">类型包含的属性</param>
        /// <returns></returns>
        public static Type CreateType(params Property[] properties)
        {
            lock (DynamicType.SyncRoot)
            {
                var dynamicTypeInfo = DynamicType.DynamicTypeInfoList.Find(item => item.PropertyEquals(properties));
                if (dynamicTypeInfo == null)
                {
                    var type = DynamicType.NewType(properties);
                    dynamicTypeInfo = new DynamicTypeInfo() { Properties = properties, BuildType = type };
                    DynamicType.DynamicTypeInfoList.Add(dynamicTypeInfo);
                }
                return dynamicTypeInfo.BuildType;
            }
        }
    }
}
